package repository

import (
	"context"
	"strconv"

	"gitee.com/thewalkers/refactoring-code/model"
	"gitee.com/thewalkers/refactoring-code/model/apperrors"
	"github.com/jmoiron/sqlx"
	"go.uber.org/zap"
)

// MysqlEmployeeRepository is data/repository implementation
// of service layer EmployeeRepository
type MysqlEmployeeRepository struct {
	DB *sqlx.DB
}

// NewEmployeeRepository is a factory for initializing Employee Repository
func NewEmployeeRepository(db *sqlx.DB) model.EmployeeRespository {
	return &MysqlEmployeeRepository{
		DB: db,
	}
}

// Create reaches out to database SQLX api
func (m *MysqlEmployeeRepository) Create(ctx context.Context, e *model.Employee) error {
	query := `INSERT INTO employees (name, address) VALUES(?, ?)`

	if _, err := m.DB.Exec(query, e.Name, e.Address); err != nil {
		zap.L().Error("repository cound not create a employee with err: ", zap.Error(err))
		return apperrors.NewInternal()
	}

	return nil
}

// FindByID fetches employee by id
func (m *MysqlEmployeeRepository) FindByID(ctx context.Context, id int64) (*model.Employee, error) {
	employee := &model.Employee{}

	query := `SELECT * FROM employees WHERE id = ?`

	// we need to actually check errors as it could be something other than not found
	if err := m.DB.Get(employee, query, id); err != nil {
		zap.L().Error("repository cound not get a employee with err: ", zap.Error(err))
		return employee, apperrors.NewNotFound("id", strconv.Itoa(int(id)))
	}

	return employee, nil
}

// UpdateByID update employee
func (m *MysqlEmployeeRepository) UpdateByID(ctx context.Context, e *model.Employee) error {
	query := `UPDATE employees SET name = ?, address = ? WHERE id = ?`

	if _, err := m.DB.Exec(query, e.Name, e.Address, e.ID); err != nil {
		zap.L().Error("repository cound not get a employee with err: ", zap.Error(err))
		return err
	}

	return nil
}

func (m *MysqlEmployeeRepository) DeleteByID(ctx context.Context, id int64) error {
	query := `DELETE FROM employees WHERE id = ?`
	if _, err := m.DB.Exec(query, id); err != nil {
		zap.L().Error("repository cound not delete a Employee with err: ", zap.Error(err))
		return apperrors.NewNotFound("id", strconv.Itoa(int(id)))
	}

	return nil
}

func (m *MysqlEmployeeRepository) GetEmployeeList(ctx context.Context, offset, limit int64) ([]*model.Employee, error) {
	query := `SELECT * FROM employees LIMIT ?, ?`

	data := make([]*model.Employee, 0, 2)
	err := m.DB.Select(&data, query, (offset-1)*limit, limit)

	return data, err
}

/*
	Here is create three
*/

func (m *MysqlEmployeeRepository) SetClassification(ctx context.Context, p *model.PaymentClassification) error {
	query := `INSERT INTO payment_classifications(employee_id) VALUES(?)`
	if _, err := m.DB.Exec(query, p.EmployeeID); err != nil {
		zap.L().Error("repository cound not create a payment_classifications( with err: ", zap.Error(err))
		return apperrors.NewInternal()
	}
	return nil
}

func (m *MysqlEmployeeRepository) SetSchedule(ctx context.Context, p *model.PaymentSchedule) error {
	query := `INSERT INTO payment_schedules(employee_id) VALUES(?)`
	if _, err := m.DB.Exec(query, p.EmployeeID); err != nil {
		zap.L().Error("repository cound not create a payment_schedules( with err: ", zap.Error(err))
		return apperrors.NewInternal()
	}
	return nil
}

func (m *MysqlEmployeeRepository) SetMethod(ctx context.Context, p *model.PaymentMethod) error {
	query := `INSERT INTO payment_methods(employee_id) VALUES(?)`
	if _, err := m.DB.Exec(query, p.EmployeeID); err != nil {
		zap.L().Error("repository cound not create a payment_methods( with err: ", zap.Error(err))
		return apperrors.NewInternal()
	}
	return nil
}

/*
	Here is get three
*/

func (m *MysqlEmployeeRepository) GetClassification(ctx context.Context, id int64) (*model.PaymentClassification, error) {
	paymentClassification := &model.PaymentClassification{}

	query := `SELECT * FROM payment_classifications WHERE id = ?`

	// we need to actually check errors as it could be something other than not found
	if err := m.DB.Get(paymentClassification, query, id); err != nil {
		zap.L().Error("repository cound not get a paymentClassification with err: ", zap.Error(err))
		return paymentClassification, apperrors.NewNotFound("id", strconv.Itoa(int(id)))
	}

	return paymentClassification, nil
}

func (m *MysqlEmployeeRepository) GetSchedule(ctx context.Context, id int64) (*model.PaymentSchedule, error) {
	paymentSchedule := &model.PaymentSchedule{}

	query := `SELECT * FROM payment_schedules WHERE id = ?`

	// we need to actually check errors as it could be something other than not found
	if err := m.DB.Get(paymentSchedule, query, id); err != nil {
		zap.L().Error("repository cound not get a payment_schedules with err: ", zap.Error(err))
		return paymentSchedule, apperrors.NewNotFound("id", strconv.Itoa(int(id)))
	}

	return paymentSchedule, nil
}

func (m *MysqlEmployeeRepository) GetMethod(ctx context.Context, id int64) (*model.PaymentMethod, error) {
	paymentMethod := &model.PaymentMethod{}

	query := `SELECT * FROM payment_methods WHERE id = ?`

	// we need to actually check errors as it could be something other than not found
	if err := m.DB.Get(paymentMethod, query, id); err != nil {
		zap.L().Error("repository cound not get a payment_methods with err: ", zap.Error(err))
		return paymentMethod, apperrors.NewNotFound("id", strconv.Itoa(int(id)))
	}

	return paymentMethod, nil
}
